package com.qf.smsplatform.search.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.smsplatform.common.enums.SmsPlatformEnums;
import com.qf.smsplatform.common.exception.SmsPlatfromEx;
import com.qf.smsplatform.common.model.StandardReport;
import com.qf.smsplatform.common.model.StandardSubmit;
import com.qf.smsplatform.search.service.SearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author: LeeRui
 * 2021/2/2 下午 8:33
 **/
@Service
@Slf4j
public class SearchServiceImpl implements SearchService {

    //注入ES连接
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //从git仓库中引入索引名称前缀
    @Value("${log-index}")
    private String index;

    @Autowired
    private ObjectMapper objectMapper;

    //将日志中接收到的StandardSubmit对象通过保存到ES
    @Override
    public void saveLog(StandardSubmit standardSubmit) throws IOException {

        //获取索引名称
        String indexName = index + new SimpleDateFormat("yyyy").format(new Date());

        //获取request
        IndexRequest request = new IndexRequest();
        request.index(indexName);
        //若为null说明是策略模块发送的消息，否则是网关模块发送的消息(SequenceId在网关模块封装到submit的)

        request.id(standardSubmit.getSequenceId());
        //将submit对象转为JSON字符串封装到request中
        request.source(objectMapper.writeValueAsString(standardSubmit), XContentType.JSON);

        //执行操作将submit存到es并响应结果
        IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);

        //根据响应结果判断是否成功
        if (!response.getResult().toString().equalsIgnoreCase("created")) {
            log.error("【搜索模块】  添加submit到ES中失败！！! result = {},submit = {}", response.getResult().toString(), standardSubmit);
            throw new SmsPlatfromEx(SmsPlatformEnums.SEARCH_SAVE_LOG_ERROR);
        }

    }

    //更新ES中Report对象信息
    @Override
    public void updateReport(StandardReport standardReport) throws IOException {

        //获取索引名称
        String indexName = index + new SimpleDateFormat("yyyy").format(new Date());

        //获取updateRequest
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index(indexName);
        updateRequest.id(standardReport.getSequenceId());

        //封装
        Map<String, Object> source = new HashMap<>();
        source.put("reportState", standardReport.getState());
        source.put("errorCode", standardReport.getErrorCode());
        updateRequest.doc(source);

        //执行更新操作并获取结果
        UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);

        //根据结果判断是否更新成功
        if (!response.getResult().toString().equalsIgnoreCase("updated")) {
            log.error("【搜索模块】 更新ES中报告信息失败！！! result = {},report = {}", response.getResult(), standardReport);
            throw new SmsPlatfromEx(SmsPlatformEnums.SEARCH_UPDATE_REPORT_ERROR);
        }

    }

    //从ES查询短信条数
    @Override
    public Long findLogCount(Map map) throws IOException {
        //获取索引名称        TODO 根据parameter中的startTime和endTime判断查询的哪些索引！！！
        String indexName = index + new SimpleDateFormat("yyyy").format(new Date());

        //获取request对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexName);

        //封装参数
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //设置查询数据的条件    clientID客户id,keyword关键字,mobile手机号,startTime开始时间,endTime结束时间
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //clientID客户id
        Integer clientID = (Integer) map.get("clientID");
        if (clientID != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("clientID", clientID));
        }
        //keyword关键字
        String keyword = (String) map.get("keyword");
        if (!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("messageContent", keyword));
        }
        //mobile手机号
        String mobile = (String) map.get("mobile");
        if (!StringUtils.isEmpty(mobile)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("destMobile", mobile));
        }
        //startTime开始时间,endTime结束时间
        Long startTime = (Long) map.get("startTime");
        Long endTime = (Long) map.get("endTime");
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("sendTime");
        if (startTime != null) {
            rangeQuery.gte(startTime);  //大于等于开始时间
        }
        if (endTime != null) {
            rangeQuery.lte(endTime);  //小于等于结束时间
        }
        boolQueryBuilder.must(rangeQuery);

        //设置给sourceBuilder
        searchSourceBuilder.query(boolQueryBuilder);

        //设置分页信息
        Integer start = (Integer) map.get("start");
        Integer rows = (Integer) map.get("rows");
        searchSourceBuilder.from(start);    //start是当前页码
        searchSourceBuilder.size(rows);    //rows是每页数据条数

        //设置高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("messageContent");
        highlightBuilder.preTags((String) map.get("highLightPreTag"));
        highlightBuilder.postTags((String) map.get("highLightPostTag"));
        searchSourceBuilder.highlighter(highlightBuilder);

        searchRequest.source(searchSourceBuilder);

        //执行查询并响应结果
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //返回短信条数
        long count = searchResponse.getHits().getTotalHits().value;

        return count;
    }


    //查询短信信息并显示
    @Override
    public List<Map> findLogInfo(Map map) throws IOException {
        //获取索引名称        TODO 根据parameter中的startTime和endTime判断查询的哪些索引！！！
        String indexName = index + new SimpleDateFormat("yyyy").format(new Date());

        //获取request对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexName);

        //封装参数
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //设置查询条件  clientID客户id,keyword关键字,mobile手机号,startTime开始时间,endTime结束时间
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //clientID客户id
        Integer clientID = (Integer) map.get("clientID");
        if (clientID != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("clientID", clientID));
        }
        // keyword关键字
        String keyword = (String) map.get("keyword");
        if (!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("messageContent", keyword));
        }
        // mobile手机号
        String mobile = (String) map.get("mobile");
        if (!StringUtils.isEmpty(mobile)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("destMobile", mobile));
        }
        //startTime开始时间,endTime结束时间
        Long startTime = (Long) map.get("startTime");
        Long endTime = (Long) map.get("endTime");
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("sendTime");
        if (startTime != null) {
            rangeQuery.gte(startTime);  //大于等于开始时间
        }
        if (endTime != null) {
            rangeQuery.lte(endTime);  //小于等于结束时间
        }
        boolQueryBuilder.must(rangeQuery);

        //设置给sourceBuilder
        searchSourceBuilder.query(boolQueryBuilder);

        //设置分页信息
        Integer start = (Integer) map.get("start");
        Integer rows = (Integer) map.get("rows");
        searchSourceBuilder.from(start);    //start是当前页码-1
        searchSourceBuilder.size(rows);    //rows是每页数据条数

        //设置高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("messageContent");
        highlightBuilder.preTags((String) map.get("highLightPreTag"));
        highlightBuilder.postTags((String) map.get("highLightPostTag"));
        searchSourceBuilder.highlighter(highlightBuilder);


        searchRequest.source(searchSourceBuilder);

        //执行查询并响应结果
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //封装数据
        List<Map> data = new ArrayList<>();
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            //高亮显示数据
            HighlightField messageContentField = hit.getHighlightFields().get("messageContent");
            if (messageContentField != null) {
                String messageContent = messageContentField.fragments()[0].string();
                sourceAsMap.put("messageContent", messageContent);
            }
            data.add(sourceAsMap);
        }
        return data;
    }


    //按短信发送状态统计短信数据配合前端以饼状图显示
    @Override
    public Map<String, Long> statistics(Map map) throws IOException {

        //获取索引名称    TODO 根据parameter中的startTime和endTime判断查询的哪些索引
        String indexName = index + new SimpleDateFormat("yyyy").format(new Date());

        //获取request对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexName);

        //封装参数
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //设置聚合函数统计各状态短信条数查询
        searchSourceBuilder.aggregation(AggregationBuilders.histogram("agg").field("reportState").interval(1));

        //设置查询条件  clientID客户id,keyword关键字,mobile手机号,startTime开始时间,endTime结束时间
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //clientID客户id
        Integer clientID = (Integer) map.get("clientID");
        if (clientID != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("clientID", clientID));
        }
        //startTime开始时间,endTime结束时间
        Long startTime = (Long) map.get("startTime");
        Long endTime = (Long) map.get("endTime");
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("sendTime");
        if (startTime != null) {
            rangeQuery.gte(startTime);  //大于等于开始时间
        }
        if (endTime != null) {
            rangeQuery.lte(endTime);  //小于等于结束时间
        }
        boolQueryBuilder.must(rangeQuery);
        searchSourceBuilder.query(boolQueryBuilder);

        //将searchSourceBuilder传给request作为查询条件配置
        searchRequest.source(searchSourceBuilder);

        //执行查询并返回响应结果
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        Aggregations aggregations = searchResponse.getAggregations();
        Histogram agg = aggregations.get("agg");
        List<? extends Histogram.Bucket> buckets = agg.getBuckets();

        //封装数据
        Map<String, Long> data = new HashMap<>();
        for (Histogram.Bucket bucket : buckets) {
            Double key = (Double) bucket.getKey();
            if (key == 0.0) {   //短信发送状态为成功
                data.put("0", bucket.getDocCount());
            } else if (key == 1.0) {   //短信发送状态为等待
                data.put("1", bucket.getDocCount());
            } else {      //短信发送状态为失败
                data.put("2", bucket.getDocCount());
            }
        }
        return data;
    }
}
